#!@BASH_PATH@
#
# ocf:pacemaker:ClusterMon resource agent
#
# Original copyright 2004 SUSE LINUX AG, Lars Marowsky-Br<E9>e
# Later changes copyright 2008-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# (GPLv2) WITHOUT ANY WARRANTY.
#

# Starts crm_mon in background which logs cluster status as
# html to the specified file.

#######################################################################
# Initialization:
: ${OCF_FUNCTIONS:="${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs"}
. "${OCF_FUNCTIONS}"
: ${__OCF_ACTION:="$1"}

# Explicitly list all environment variables used, to make static analysis happy
: ${OCF_RESKEY_user:=""}
: ${OCF_RESKEY_pidfile:="/tmp/ClusterMon_${OCF_RESOURCE_INSTANCE}.pid"}
: ${OCF_RESKEY_update:="15000"}
: ${OCF_RESKEY_extra_options:=""}
: ${OCF_RESKEY_htmlfile:="/tmp/ClusterMon_${OCF_RESOURCE_INSTANCE}.html"}

#######################################################################

meta_data() {
    cat <<END
<?xml version="1.0"?>
<resource-agent name="ClusterMon" version="@VERSION@">
<version>1.1</version>

<longdesc lang="en">
This is a ClusterMon Resource Agent.
It outputs current cluster status to the html.
</longdesc>
<shortdesc lang="en">Runs crm_mon in the background, recording the cluster status to an HTML file</shortdesc>

<parameters>

<parameter name="user">
<longdesc lang="en">
The user we want to run crm_mon as
</longdesc>
<shortdesc lang="en">The user we want to run crm_mon as</shortdesc>
<content type="string" default="root" />
</parameter>

<parameter name="update">
<longdesc lang="en">
How frequently should we update the cluster status (in milliseconds).
For compatibility with old documentation, values less than 1000 will be treated
as seconds.
</longdesc>
<shortdesc lang="en">Update interval in milliseconds</shortdesc>
<content type="integer" default="15000" />
</parameter>

<parameter name="extra_options">
<longdesc lang="en">
Additional options to pass to crm_mon.  Eg. -n -r
</longdesc>
<shortdesc lang="en">Extra options</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="pidfile" unique-group="pidfile">
<longdesc lang="en">
PID file location to ensure only one instance is running
</longdesc>
<shortdesc lang="en">PID file</shortdesc>
<content type="string" default="/tmp/ClusterMon_${OCF_RESOURCE_INSTANCE}.pid" />
</parameter>

<parameter name="htmlfile" unique-group="htmlfile" required="0">
<longdesc lang="en">
Location to write HTML output to.
</longdesc>
<shortdesc lang="en">HTML output</shortdesc>
<content type="string" default="/tmp/ClusterMon_${OCF_RESOURCE_INSTANCE}.html" />
</parameter>
</parameters>

<actions>
<action name="start"   timeout="20s" />
<action name="stop"    timeout="20s" />
<action name="monitor" depth="0"  timeout="20s" interval="10s" />
<action name="meta-data"  timeout="5s" />
<action name="validate-all"  timeout="30s" depth="0" />
</actions>
</resource-agent>
END
}

#######################################################################

ClusterMon_usage() {
    cat <<END
usage: $0 {start|stop|monitor|validate-all|meta-data}

Expects to have a fully populated OCF RA-compliant environment set.
END
}

ClusterMon_exit() {
    if [ $1 -ne 0 ]; then
        exit $OCF_ERR_GENERIC
    else
        exit $OCF_SUCCESS
    fi
}

ClusterMon_start() {
    if [ -n "$OCF_RESKEY_user" ]; then
        su - "$OCF_RESKEY_user" -c "$CMON_CMD"
    else
        eval $CMON_CMD
    fi
    ClusterMon_exit $?
}

ClusterMon_stop() {
    if [ -f "$OCF_RESKEY_pidfile" ]; then
        pid=$(cat "$OCF_RESKEY_pidfile")
        if [ -n "$pid" ]; then
            kill -s 9 $pid
            rm -f "$OCF_RESKEY_pidfile"
        fi
    fi
    ClusterMon_exit 0
}

ClusterMon_monitor() {
    local USERARG=""
    local header
    local pid

    if [ -f "$OCF_RESKEY_pidfile" ]; then
        pid=$(cat "$OCF_RESKEY_pidfile")
        if [ -n "$pid" ]; then
            if [ -n "$OCF_RESKEY_user" ]; then
                USERARG="-u $OCF_RESKEY_user"
            fi

            # Use column header wide as command, to ensure it's shown in full
            header=$(echo $CMON_CMD | tr 'crmon, \t' 'xxxxxxxx')

            ps $USERARG -o "args=${header}" -p $pid 2>/dev/null | \
                grep -qE "[c]rm_mon.*${OCF_RESKEY_pidfile}"

            case $? in
                0) exit $OCF_SUCCESS;;
                1) exit $OCF_NOT_RUNNING;;
                *) exit $OCF_ERR_GENERIC;;
            esac
        fi
    fi
    exit $OCF_NOT_RUNNING
}

CheckOptions() {
while getopts Vi:nrh:cdp: OPTION
do
    case "$OPTION" in
    V|n|r|c|d);;
    i)  ocf_log warn "You should not have specified the -i option, since OCF_RESKEY_update is set already!";;
    h)  ocf_log warn "You should not have specified the -h option, since OCF_RESKEY_htmlfile is set already!";;
    p)  ocf_log warn "You should not have specified the -p option, since OCF_RESKEY_pidfile is set already!";;
    *)  return $OCF_ERR_ARGS;;
    esac

    case "$OCF_RESKEY_extra_options" in
    *--output-as*)  ocf_log warn "You should not have specified the -output-as option, since OCF_RESKEY_htmlfile is set already!";;
    *--output-to*)  ocf_log warn "You should not have specified the -output-to option, since OCF_RESKEY_htmlfile is set already!";;
    esac
done

if [ $? -ne 0 ]; then
    return $OCF_ERR_ARGS
fi

# We should have eaten all options at this stage
shift $(($OPTIND -1))
if [ $# -gt 0 ]; then
    false
else
    true
fi
}

ClusterMon_validate() {
    # Host-specific checks
    if [ "$OCF_CHECK_LEVEL" = "10" ]; then

        # Existence of the user
        if [ -n "$OCF_RESKEY_user" ]; then
            getent passwd "$OCF_RESKEY_user" >/dev/null
            if [ $? -eq 0 ]; then
                : Yes, user exists. We can further check his permission on crm_mon if necessary
            else
                ocf_log err "The user $OCF_RESKEY_user does not exist!"
                exit $OCF_ERR_ARGS
            fi
        fi
    fi

    # Pidfile should be an absolute path
    case "$OCF_RESKEY_pidfile" in
        /*) ;;
        *) ocf_log warn "pidfile ($OCF_RESKEY_pidfile) is not an absolute path" ;;
    esac

    # Check the update interval
    if ocf_is_decimal "$OCF_RESKEY_update" && [ $OCF_RESKEY_update -gt 0 ]; then
        :
    else
        ocf_log err "Invalid update interval $OCF_RESKEY_update. It should be positive integer!"
        exit $OCF_ERR_ARGS
    fi

    if CheckOptions $OCF_RESKEY_extra_options; then
        :
    else
        ocf_log err "Invalid options $OCF_RESKEY_extra_options!"
        exit $OCF_ERR_ARGS
    fi

    # Htmlfile should be an absolute path
    case "$OCF_RESKEY_htmlfile" in
        /*) ;;
        *) ocf_log warn "htmlfile ($OCF_RESKEY_htmlfile) is not an absolute path" ;;
    esac

    echo "Validate OK"
    return $OCF_SUCCESS
}

if [ $# -ne 1 ]; then
    ClusterMon_usage
    exit $OCF_ERR_ARGS
fi

if [ ${OCF_RESKEY_update} -ge 1000 ]; then
    OCF_RESKEY_update=$(( $OCF_RESKEY_update / 1000 ))
fi

CMON_CMD="${HA_SBIN_DIR}/crm_mon -p \"$OCF_RESKEY_pidfile\" -d -i $OCF_RESKEY_update $OCF_RESKEY_extra_options --output-as=html --output-to=\"$OCF_RESKEY_htmlfile\""

case "$__OCF_ACTION" in
meta-data)      meta_data
                exit $OCF_SUCCESS
                ;;
start)          ClusterMon_start
                ;;
stop)           ClusterMon_stop
                ;;
monitor)        ClusterMon_monitor
                ;;
validate-all)   ClusterMon_validate
                ;;
usage|help)     ClusterMon_usage
                exit $OCF_SUCCESS
                ;;
*)              ClusterMon_usage
                exit $OCF_ERR_UNIMPLEMENTED
                ;;
esac

exit $?

# vim: set filetype=sh expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=80:
